mod bus;
mod check;
mod search;

#[cfg(test)]
mod test;

use search::*;
use std::collections::HashSet;
use check::check_coord_for_grid;

pub fn route_single_target_path(
    grid: &mut Grid,
    start: Coord,
    target: Coord,
    color: Color,
) -> Result<Vec<Coord>, String> {
    let path = search_single_target_path(grid, start, target)?;
    for coord in &path {
        grid.set_cell(*coord, Cell::Routed(color));
    }
    Ok(path)
}

pub fn route_single_target_path_with_astar(
    grid: &mut Grid,
    start: Coord,
    target: Coord,
    color: Color,
) -> Result<Vec<Coord>, String> {
    let path = search_single_target_path_with_astar(grid, start, target)?;
    for coord in &path {
        grid.set_cell(*coord, Cell::Routed(color));
    }
    Ok(path)
}

pub fn route_multi_targets_path(
    grid: &mut Grid,
    start: Coord,
    targets: &[Coord],
    color: Color,
) -> Result<HashSet<Coord>, String> {
    let path = search_multi_targets_path(grid, start, targets)?;
    for coord in &path {
        grid.set_cell(*coord, Cell::Routed(color));
    }
    Ok(path)
}

pub fn route_multi_targets_path_with_astar(
    grid: &mut Grid,
    start: Coord,
    targets: &[Coord],
    color: Color,
) -> Result<HashSet<Coord>, String> {
    let path = search_multi_targets_path_with_astar(grid, start, targets)?;
    for coord in &path {
        grid.set_cell(*coord, Cell::Routed(color));
    }
    Ok(path)
}

pub fn route_coords_to_a_path(
    grid: &mut Grid,
    coords: &[Coord],
    color: Color,
) -> Result<Vec<Coord>, String> {
    for coord in coords.iter() {
        check_coord_for_grid(grid, *coord)?;
    }

    if coords.len() < 2 {
        return Err("'coords' needs at least two coord as a path!".into());
    }

    let mut total_path = Vec::new();
    let mut total_expended = HashSet::new();
    for (start, target) in coords.iter().zip(coords.iter().skip(1)) {
        match search_single_target_path_with_astar_show_expend(grid, *start, *target) {
            Ok((path, expended)) => {
                for coord in path.iter() {
                    grid.set_cell(*coord, Cell::Routed(color));
                }
                total_path.extend(path.into_iter());
                total_expended.extend(expended.into_iter());
            }
            Err(err) => {
                for coord in total_path {
                    grid.set_cell(coord, Cell::Empty);
                }
                return Err(err);
            }
        }
    }

    for coord in total_expended {
        let cell = grid.cell(coord).unwrap();
        if cell.is_empty() {
            grid.set_cell(coord, Cell::Routed(Color::gray()));
        }

    }

    Ok(total_path)
}

pub use bus::*;
